//! 文件输出测试 - Epic 5 ATDD
//!
//! 测试 FR18: 系统支持文件日志输出
//! 测试 FR19: 用户可配置日志文件路径
//! 测试 FR20: 系统自动创建日志文件所在目录
//! 测试 FR21: 用户可配置文件输出的日志级别过滤
//! 测试 FR22: 系统支持文件追加写入模式
//!
//! ATDD 状态: RED (测试先行，等待实现)
//! 生成日期: 2025-12-02
//! 生成者: TEA Agent

mod common;

use common::TempTestDir;
use log4r::appender::Appender;
pub use log4r::appender::FileAppender;
use log4r::{Level, LogEvent};
use serial_test::serial;
use std::fs;
use std::sync::Arc;
use std::thread;

// ============================================================================
// AC-5.1: 基础文件输出 [P0]
// ============================================================================

/// IT-5.1-001: 基础文件写入测试
/// AC-5.1: FileAppender 配置了文件路径后，日志被写入指定文件
#[test]
#[serial]
fn test_file_appender_basic_write() {
    // GIVEN: 临时目录和文件路径
    let temp_dir = TempTestDir::new("file_basic").expect("创建临时目录失败");
    let log_path = temp_dir.path().join("app.log");

    // WHEN: 创建 FileAppender 并写入日志
    let appender = FileAppender::new(&log_path).expect("创建 FileAppender 失败");
    let event = LogEvent::new(Level::Info, "test_app", "基础文件写入测试");
    appender.write(&event).expect("写入日志失败");
    appender.flush().expect("刷新失败");

    // THEN: 日志应写入文件
    let content = fs::read_to_string(&log_path).expect("读取日志文件失败");
    assert!(content.contains("基础文件写入测试"), "日志内容应包含消息");
}

/// IT-5.1-002: 格式化输出验证
/// AC-5.1: 输出使用 Formatter 格式化
#[test]
#[serial]
fn test_file_appender_formatted_output() {
    // GIVEN: FileAppender
    let temp_dir = TempTestDir::new("file_format").expect("创建临时目录失败");
    let log_path = temp_dir.path().join("formatted.log");

    // WHEN: 写入日志
    let appender = FileAppender::new(&log_path).expect("创建 FileAppender 失败");
    let event = LogEvent::new(Level::Warn, "format_test", "格式化测试消息");
    appender.write(&event).expect("写入日志失败");
    appender.flush().expect("刷新失败");

    // THEN: 输出应包含格式化的级别和目标
    let content = fs::read_to_string(&log_path).expect("读取日志文件失败");
    assert!(content.contains("[WARN ]"), "应包含格式化的级别");
    assert!(content.contains("format_test"), "应包含目标模块");
    assert!(content.contains("格式化测试消息"), "应包含消息内容");
}

/// IT-5.1-003: 每条日志后自动换行
/// AC-5.1: 每条日志后自动换行
#[test]
#[serial]
fn test_file_appender_auto_newline() {
    // GIVEN: FileAppender
    let temp_dir = TempTestDir::new("file_newline").expect("创建临时目录失败");
    let log_path = temp_dir.path().join("newline.log");

    // WHEN: 写入多条日志
    let appender = FileAppender::new(&log_path).expect("创建 FileAppender 失败");
    for i in 0..3 {
        let event = LogEvent::new(Level::Info, "newline_test", format!("消息 {}", i));
        appender.write(&event).expect("写入日志失败");
    }
    appender.flush().expect("刷新失败");

    // THEN: 每条日志应在单独的行
    let content = fs::read_to_string(&log_path).expect("读取日志文件失败");
    let lines: Vec<&str> = content.lines().collect();
    assert_eq!(lines.len(), 3, "应有 3 行日志");
    assert!(lines[0].contains("消息 0"));
    assert!(lines[1].contains("消息 1"));
    assert!(lines[2].contains("消息 2"));
}

/// IT-5.1-004: 多线程并发写入测试
/// AC-5.1: write 操作是线程安全的
#[test]
#[serial]
fn test_file_appender_concurrent_writes() {
    // GIVEN: 共享的 FileAppender
    let temp_dir = TempTestDir::new("file_concurrent").expect("创建临时目录失败");
    let log_path = temp_dir.path().join("concurrent.log");
    let appender = Arc::new(FileAppender::new(&log_path).expect("创建 FileAppender 失败"));
    let mut handles = vec![];

    // WHEN: 多线程并发写入
    for i in 0..4 {
        let appender_clone = Arc::clone(&appender);
        let handle = thread::spawn(move || {
            for j in 0..10 {
                let event = LogEvent::new(
                    Level::Info,
                    "concurrent_test",
                    format!("Thread {} - Message {}", i, j),
                );
                appender_clone.write(&event).expect("并发写入失败");
            }
        });
        handles.push(handle);
    }

    // 等待所有线程完成
    for handle in handles {
        handle.join().expect("线程 join 失败");
    }
    appender.flush().expect("刷新失败");

    // THEN: 所有日志应写入文件（共 40 条）
    let content = fs::read_to_string(&log_path).expect("读取日志文件失败");
    let lines: Vec<&str> = content.lines().collect();
    assert_eq!(lines.len(), 40, "应有 40 条日志");
}

// ============================================================================
// AC-5.2: 文件路径配置 [P1]
// ============================================================================

/// IT-5.2-001: 相对路径测试
/// AC-5.2: 支持相对路径
#[test]
#[serial]
fn test_file_appender_relative_path() {
    // GIVEN: 相对路径
    let temp_dir = TempTestDir::new("file_relative").expect("创建临时目录失败");
    let relative_path = temp_dir.path().join("logs/app.log");

    // WHEN: 使用相对路径创建 FileAppender
    let appender = FileAppender::new(&relative_path).expect("创建 FileAppender 失败");
    let event = LogEvent::new(Level::Info, "path_test", "相对路径测试");
    appender.write(&event).expect("写入日志失败");
    appender.flush().expect("刷新失败");

    // THEN: 文件应在正确位置创建
    assert!(relative_path.exists(), "日志文件应存在");
    let content = fs::read_to_string(&relative_path).expect("读取日志文件失败");
    assert!(content.contains("相对路径测试"));
}

/// IT-5.2-002: 绝对路径测试
/// AC-5.2: 支持绝对路径
#[test]
#[serial]
fn test_file_appender_absolute_path() {
    // GIVEN: 绝对路径
    let temp_dir = TempTestDir::new("file_absolute").expect("创建临时目录失败");
    let absolute_path = temp_dir.path().canonicalize().unwrap().join("absolute.log");

    // WHEN: 使用绝对路径创建 FileAppender
    let appender = FileAppender::new(&absolute_path).expect("创建 FileAppender 失败");
    let event = LogEvent::new(Level::Info, "path_test", "绝对路径测试");
    appender.write(&event).expect("写入日志失败");
    appender.flush().expect("刷新失败");

    // THEN: 文件应在正确位置创建
    assert!(absolute_path.exists(), "日志文件应存在");
}

// ============================================================================
// AC-5.3: 目录自动创建 [P0]
// ============================================================================

/// IT-5.3-001: 目录自动创建测试
/// AC-5.3: 配置的日志路径目录不存在时，自动创建所有必要的父目录
#[test]
#[serial]
fn test_file_appender_auto_create_directory() {
    // GIVEN: 不存在的目录路径
    let temp_dir = TempTestDir::new("file_mkdir").expect("创建临时目录失败");
    let nested_path = temp_dir.path().join("logs/app.log");
    assert!(!nested_path.parent().unwrap().exists(), "目录应不存在");

    // WHEN: 创建 FileAppender
    let result = FileAppender::new(&nested_path);

    // THEN: 应成功创建，目录应存在
    assert!(result.is_ok(), "创建 FileAppender 应成功");
    assert!(nested_path.parent().unwrap().exists(), "目录应被自动创建");
}

/// IT-5.3-002: 多级目录创建测试
/// AC-5.3: 自动创建所有必要的父目录
#[test]
#[serial]
fn test_file_appender_create_nested_directories() {
    // GIVEN: 多级不存在的目录路径
    let temp_dir = TempTestDir::new("file_nested").expect("创建临时目录失败");
    let deep_path = temp_dir.path().join("level1/level2/level3/app.log");

    // WHEN: 创建 FileAppender
    let appender = FileAppender::new(&deep_path).expect("创建 FileAppender 失败");
    let event = LogEvent::new(Level::Info, "nested_test", "多级目录测试");
    appender.write(&event).expect("写入日志失败");
    appender.flush().expect("刷新失败");

    // THEN: 所有目录应被创建
    assert!(deep_path.exists(), "日志文件应存在");
}

/// IT-5.3-003: 目录创建失败测试
/// AC-5.3, AC-5.6: 目录创建失败时返回 Log4rError::IoError
#[test]
#[serial]
#[cfg(unix)]
fn test_file_appender_directory_creation_failure() {
    // GIVEN: 无权限创建的路径（Unix 系统）
    let invalid_path = std::path::Path::new("/root/log4r_test_no_permission/app.log");

    // WHEN: 尝试创建 FileAppender
    let result = FileAppender::new(invalid_path);

    // THEN: 应返回错误
    assert!(result.is_err(), "应返回错误");
}

// ============================================================================
// AC-5.4: 文件级别过滤 [P1]
// ============================================================================

/// UT-5.4-001: 级别过滤 - 低于阈值被过滤
/// AC-5.4: 低于配置级别的日志被过滤，不写入文件
#[test]
#[serial]
fn test_file_appender_level_filter_below_threshold() {
    // GIVEN: FileAppender 配置级别为 WARN
    let temp_dir = TempTestDir::new("file_filter_below").expect("创建临时目录失败");
    let log_path = temp_dir.path().join("filter.log");
    let appender = FileAppender::new(&log_path)
        .expect("创建 FileAppender 失败")
        .with_level(Level::Warn);

    // WHEN: 写入 INFO 级别日志
    let event = LogEvent::new(Level::Info, "filter_test", "INFO 消息应被过滤");
    appender.write(&event).expect("写入应成功");
    appender.flush().expect("刷新失败");

    // THEN: 日志被过滤，文件为空或不包含该消息
    let content = fs::read_to_string(&log_path).unwrap_or_default();
    assert!(!content.contains("INFO 消息应被过滤"), "INFO 消息应被过滤");
}

/// UT-5.4-002: 级别过滤 - 等于阈值通过
/// AC-5.4: 等于配置级别的日志正常写入文件
#[test]
#[serial]
fn test_file_appender_level_filter_at_threshold() {
    // GIVEN: FileAppender 配置级别为 WARN
    let temp_dir = TempTestDir::new("file_filter_at").expect("创建临时目录失败");
    let log_path = temp_dir.path().join("filter.log");
    let appender = FileAppender::new(&log_path)
        .expect("创建 FileAppender 失败")
        .with_level(Level::Warn);

    // WHEN: 写入 WARN 级别日志
    let event = LogEvent::new(Level::Warn, "filter_test", "WARN 消息应通过");
    appender.write(&event).expect("写入应成功");
    appender.flush().expect("刷新失败");

    // THEN: 日志正常写入
    let content = fs::read_to_string(&log_path).expect("读取日志文件失败");
    assert!(content.contains("WARN 消息应通过"), "WARN 消息应写入文件");
}

/// UT-5.4-003: 级别过滤 - 高于阈值通过
/// AC-5.4: 高于配置级别的日志正常写入文件
#[test]
#[serial]
fn test_file_appender_level_filter_above_threshold() {
    // GIVEN: FileAppender 配置级别为 WARN
    let temp_dir = TempTestDir::new("file_filter_above").expect("创建临时目录失败");
    let log_path = temp_dir.path().join("filter.log");
    let appender = FileAppender::new(&log_path)
        .expect("创建 FileAppender 失败")
        .with_level(Level::Warn);

    // WHEN: 写入 ERROR 级别日志
    let event = LogEvent::new(Level::Error, "filter_test", "ERROR 消息应通过");
    appender.write(&event).expect("写入应成功");
    appender.flush().expect("刷新失败");

    // THEN: 日志正常写入
    let content = fs::read_to_string(&log_path).expect("读取日志文件失败");
    assert!(content.contains("ERROR 消息应通过"), "ERROR 消息应写入文件");
}

/// UT-5.4-004: 默认级别 Trace 测试
/// AC-5.4: 默认级别为 Trace（不过滤任何日志）
#[test]
#[serial]
fn test_file_appender_default_level_is_trace() {
    // GIVEN: FileAppender 使用默认配置
    let temp_dir = TempTestDir::new("file_default_level").expect("创建临时目录失败");
    let log_path = temp_dir.path().join("default.log");
    let appender = FileAppender::new(&log_path).expect("创建 FileAppender 失败");

    // THEN: 默认级别应为 Trace
    assert_eq!(appender.level(), Level::Trace, "默认级别应为 Trace");

    // WHEN: 写入 TRACE 级别日志
    let event = LogEvent::new(Level::Trace, "default_test", "TRACE 消息");
    appender.write(&event).expect("写入应成功");
    appender.flush().expect("刷新失败");

    // THEN: TRACE 日志应写入
    let content = fs::read_to_string(&log_path).expect("读取日志文件失败");
    assert!(content.contains("TRACE 消息"), "TRACE 消息应写入文件");
}

// ============================================================================
// AC-5.5: 文件追加写入 [P0]
// ============================================================================

/// IT-5.5-001: 追加写入测试
/// AC-5.5: 日志文件已存在时，新日志追加到文件末尾
#[test]
#[serial]
fn test_file_appender_append_mode() {
    // GIVEN: 已存在的日志文件
    let temp_dir = TempTestDir::new("file_append").expect("创建临时目录失败");
    let log_path = temp_dir.path().join("append.log");
    fs::write(&log_path, "existing content\n").expect("写入初始内容失败");

    // WHEN: 创建 FileAppender 并写入新日志
    let appender = FileAppender::new(&log_path).expect("创建 FileAppender 失败");
    let event = LogEvent::new(Level::Info, "append_test", "新追加的内容");
    appender.write(&event).expect("写入日志失败");
    appender.flush().expect("刷新失败");

    // THEN: 原有内容保留，新内容追加
    let content = fs::read_to_string(&log_path).expect("读取日志文件失败");
    assert!(content.starts_with("existing content\n"), "原有内容应保留");
    assert!(content.contains("新追加的内容"), "新内容应追加");
}

/// IT-5.5-002: 文件不存在自动创建测试
/// AC-5.5: 文件不存在时自动创建
#[test]
#[serial]
fn test_file_appender_create_new_file() {
    // GIVEN: 不存在的文件路径
    let temp_dir = TempTestDir::new("file_create").expect("创建临时目录失败");
    let log_path = temp_dir.path().join("new_file.log");
    assert!(!log_path.exists(), "文件应不存在");

    // WHEN: 创建 FileAppender 并写入日志
    let appender = FileAppender::new(&log_path).expect("创建 FileAppender 失败");
    let event = LogEvent::new(Level::Info, "create_test", "新文件测试");
    appender.write(&event).expect("写入日志失败");
    appender.flush().expect("刷新失败");

    // THEN: 文件应被创建
    assert!(log_path.exists(), "文件应被创建");
    let content = fs::read_to_string(&log_path).expect("读取日志文件失败");
    assert!(content.contains("新文件测试"));
}

// ============================================================================
// AC-5.6: 错误处理 [P0]
// ============================================================================

/// IT-5.6-001: 文件创建失败错误处理
/// AC-5.6: 文件创建失败返回 Log4rError::IoError
#[test]
#[serial]
#[cfg(unix)]
fn test_file_appender_creation_error() {
    // GIVEN: 无效的文件路径（Unix 系统）
    let invalid_path = std::path::Path::new("/root/no_permission/app.log");

    // WHEN: 尝试创建 FileAppender
    let result = FileAppender::new(invalid_path);

    // THEN: 应返回 IoError
    assert!(result.is_err(), "应返回错误");
    if let Err(e) = result {
        assert!(matches!(e, log4r::Log4rError::IoError(_)), "应为 IoError");
    }
}

/// IT-5.6-002: 不使用 panic/unwrap/expect
/// AC-5.6: 不使用 panic!/unwrap()/expect()
#[test]
#[serial]
fn test_file_appender_no_panic() {
    // GIVEN: FileAppender
    let temp_dir = TempTestDir::new("file_no_panic").expect("创建临时目录失败");
    let log_path = temp_dir.path().join("no_panic.log");

    // WHEN: 正常操作
    let appender = FileAppender::new(&log_path).expect("创建 FileAppender 失败");
    let event = LogEvent::new(Level::Info, "panic_test", "不应 panic");

    // THEN: 所有操作返回 Result，不 panic
    let write_result = appender.write(&event);
    let flush_result = appender.flush();
    let close_result = appender.close();

    assert!(write_result.is_ok());
    assert!(flush_result.is_ok());
    assert!(close_result.is_ok());
}

// ============================================================================
// P2: 边缘情况测试
// ============================================================================

/// UT-5.2-003: 特殊字符路径测试
/// 边缘情况: 路径包含特殊字符
#[test]
#[serial]
fn test_file_appender_special_char_path() {
    // GIVEN: 包含特殊字符的路径
    let temp_dir = TempTestDir::new("file_special").expect("创建临时目录失败");
    let special_path = temp_dir.path().join("logs-2025.12.02/app-test.log");

    // WHEN: 创建 FileAppender
    let result = FileAppender::new(&special_path);

    // THEN: 应成功创建
    assert!(result.is_ok(), "特殊字符路径应支持");
}

/// UT-5.2-004: Unicode 路径测试
/// 边缘情况: 路径包含中文字符
#[test]
#[serial]
fn test_file_appender_unicode_path() {
    // GIVEN: 包含中文的路径
    let temp_dir = TempTestDir::new("file_unicode").expect("创建临时目录失败");
    let unicode_path = temp_dir.path().join("日志/应用.log");

    // WHEN: 创建 FileAppender 并写入
    let appender = FileAppender::new(&unicode_path).expect("创建 FileAppender 失败");
    let event = LogEvent::new(Level::Info, "unicode_test", "中文消息测试");
    appender.write(&event).expect("写入日志失败");
    appender.flush().expect("刷新失败");

    // THEN: 应正常工作
    assert!(unicode_path.exists(), "Unicode 路径文件应存在");
    let content = fs::read_to_string(&unicode_path).expect("读取日志文件失败");
    assert!(content.contains("中文消息测试"));
}

/// IT-5.7-001: flush 缓冲区刷新测试
#[test]
#[serial]
fn test_file_appender_flush() {
    // GIVEN: FileAppender
    let temp_dir = TempTestDir::new("file_flush").expect("创建临时目录失败");
    let log_path = temp_dir.path().join("flush.log");
    let appender = FileAppender::new(&log_path).expect("创建 FileAppender 失败");

    // WHEN: 写入后立即 flush
    let event = LogEvent::new(Level::Info, "flush_test", "flush 测试");
    appender.write(&event).expect("写入日志失败");
    let flush_result = appender.flush();

    // THEN: flush 应成功
    assert!(flush_result.is_ok(), "flush 应成功");

    // 验证数据已写入
    let content = fs::read_to_string(&log_path).expect("读取日志文件失败");
    assert!(content.contains("flush 测试"));
}

/// IT-5.7-002: close 资源释放测试
#[test]
#[serial]
fn test_file_appender_close() {
    // GIVEN: FileAppender
    let temp_dir = TempTestDir::new("file_close").expect("创建临时目录失败");
    let log_path = temp_dir.path().join("close.log");
    let appender = FileAppender::new(&log_path).expect("创建 FileAppender 失败");

    // WHEN: 写入后 close
    let event = LogEvent::new(Level::Info, "close_test", "close 测试");
    appender.write(&event).expect("写入日志失败");
    let close_result = appender.close();

    // THEN: close 应成功
    assert!(close_result.is_ok(), "close 应成功");

    // 验证数据已写入
    let content = fs::read_to_string(&log_path).expect("读取日志文件失败");
    assert!(content.contains("close 测试"));
}

// ============================================================================
// 编译时验证
// ============================================================================

/// 编译时验证 FileAppender 实现 Send + Sync
#[test]
fn test_file_appender_is_send_sync() {
    fn assert_send_sync<T: Send + Sync>() {}
    assert_send_sync::<FileAppender>();
}

/// 编译时验证 FileAppender 实现 Appender trait
#[test]
fn test_file_appender_implements_appender() {
    fn accept_appender(_: &dyn Appender) {}
    fn accept_boxed_appender(_: Box<dyn Appender>) {}

    let temp_dir = TempTestDir::new("file_trait").expect("创建临时目录失败");
    let log_path = temp_dir.path().join("trait.log");
    let appender = FileAppender::new(&log_path).expect("创建 FileAppender 失败");

    accept_appender(&appender);
    accept_boxed_appender(Box::new(
        FileAppender::new(temp_dir.path().join("trait2.log")).unwrap(),
    ));
}
